package com.syf.core.actuator;

import com.syf.core.service.SyService;
import com.syf.core.service.SyServiceDefinition;
import com.syf.core.service.SyServiceSetting;
import com.syf.core.utils.NewInstanceByClass;
import com.syf.core.utils.classscan.AnnotationClassFilter;
import com.syf.core.utils.classscan.MixedClassFilter;
import com.syf.core.utils.classscan.ScanClassFromClassPath;
import com.syf.core.utils.classscan.TypeClassFilter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class SyServiceContainer {
    /**
     * 懒加载模式
     */
    private final boolean lazyMode;

    /**
     * service组件扫描基础路径
     */
    private final String baseDir;

    /**
     * service组件扫描器
     */
    private final ScanClassFromClassPath classScanner;

    /**
     * 缓存SyServiceDefinition的map，其中key为SyService的类路径
     */
    private final ConcurrentHashMap<String, SyServiceDefinition> serviceDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 缓存SyServiceDefinition的map，其中key为serviceCode
     */
    private final ConcurrentHashMap<String, SyServiceDefinition> serviceDefinitionMapByCode = new ConcurrentHashMap<>();

    /**
     * 缓存SyService对象实例的map，其中key为SyService的类路径
     */
    private final ConcurrentHashMap<String, SyService> cachedServiceInstanceMap = new ConcurrentHashMap<>();

    public SyServiceContainer(String scanBaseDir) {
        this(false, scanBaseDir);
    }

    public SyServiceContainer(boolean lazyMode, String baseDir) {
        this.lazyMode = lazyMode;
        this.baseDir = baseDir;
        this.classScanner = new ScanClassFromClassPath(new MixedClassFilter(new AnnotationClassFilter(SyServiceSetting.class), new TypeClassFilter(SyService.class)));

        // 加载SyService definition
        loadServiceDefines();

        // 初始SyServiceDefinitionPublisher
        SyServiceDefinitionPublisher.getInstance().initializer(serviceDefinitionMap.values());
    }

    /**
     * 根据service Class获取service实例
     */
    public SyService getService(Class<? extends SyService> serviceClz) {
        SyServiceDefinition definition = serviceDefinitionMap.get(serviceClz.getName());
        if (definition == null) {
            return null;
        }

        return getServiceInstance(definition);
    }

    /**
     * 根据serviceCode获取service实例
     */
    public SyService getService(String serviceCode) {
        SyServiceDefinition definition = serviceDefinitionMapByCode.get(serviceCode);
        if (definition == null) {
            return null;
        }

        return getServiceInstance(definition);
    }

    /**
     * 根据service Class获取service definition
     */
    public SyServiceDefinition getServiceDefinition(Class<? extends SyService> serviceClz) {
        return serviceDefinitionMap.get(serviceClz.getName());
    }

    /**
     * 根据serviceCode获取service实例
     */
    public SyServiceDefinition getServiceDefinition(String serviceCode) {
        return serviceDefinitionMapByCode.get(serviceCode);
    }

    /**
     * 初始化单例对象
     */
    void initSingleServices() {
        if (!lazyMode) {
            for (Map.Entry<String, SyServiceDefinition> entry : serviceDefinitionMap.entrySet()) {
                SyServiceDefinition definition = entry.getValue();
                if (definition.isSingle()) { // 单例才会缓存实例
                    cachedServiceInstanceMap.put(entry.getKey(), buildSyServiceInstance(definition));
                }
            }
        }
    }

    /**
     * 获取service实例，其中single类型的service instance从缓存中获取，形成单例.
     */
    public SyService getServiceInstance(SyServiceDefinition definition) {
        SyService syService;
        if (definition.isSingle()) { // 单例，则从缓存map中获取
            syService = cachedServiceInstanceMap.get(definition.getServiceClass().getName());
            // 如果是lazyMode，则第一次获取时，通过byte buddy构建SyService对象实例
            if (syService == null && lazyMode) {
                syService = buildSyServiceInstance(definition);
                cachedServiceInstanceMap.put(definition.getServiceClass().getName(), syService);
            }
        } else {
            // 非单例对象，则每次获取都新建
            syService = buildSyServiceInstance(definition);
        }

        return syService;
    }

    private void loadServiceDefines() {
        // 扫描项目中SyService接口子类并带@SyServiceSetting的Class
        Set<Class<?>> serviceClassSet = classScanner.getPackageAllClasses(baseDir, true);
        for (Class<?> clazz : serviceClassSet) {
            SyServiceDefinition definition = new SyServiceDefinition();
            Class<? extends SyService> serviceClazz = (Class<? extends SyService>) clazz;
            definition.setServiceClass(serviceClazz);

            // 通过注解获取service 设置参数: code ,desc ,isSignal,version
            SyServiceSetting annotation = serviceClazz.getAnnotation(SyServiceSetting.class);
            definition.setServiceCode(annotation.code());
            definition.setServiceDesc(annotation.desc());
            definition.setSingle(annotation.signal());
            definition.setServiceVersion(annotation.version());

            // 获取service的父类接口泛型
            Type superType = (serviceClazz.getGenericInterfaces())[0];
            Type[] actualTypeArguments = ((ParameterizedType) superType).getActualTypeArguments();
            definition.setRequestClass(getGenericType(actualTypeArguments[0]));
            definition.setResponseClass(getGenericType(actualTypeArguments[1]));

            serviceDefinitionMap.put(definition.getServiceClass().getName(), definition);
            serviceDefinitionMapByCode.put(definition.getServiceCode(), definition);
        }
    }

    /**
     * 将SyServiceDefinition通过动态反射机制形成实例对象
     */
    private SyService buildSyServiceInstance(SyServiceDefinition definition) {
        Class<? extends SyService> serviceClass = definition.getServiceClass();
        try {
            return (SyService) NewInstanceByClass.buildInstanceOnConstructor(serviceClass);
        } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                 IllegalAccessException e) {
            throw new RuntimeException("构建SyService实例出现异常", e);
        }
    }

    /**
     * 获取泛型类
     */
    private Class<?> getGenericType(Type type) {
        if (type instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) type).getRawType();
        } else {
            return (Class<?>) type;
        }
    }
}
